package com.spikything.video {
	
	import com.spikything.gfx.Rect;
	import flash.display.BlendMode;
	import flash.display.DisplayObject;
    import flash.display.Sprite;
	import flash.display.Stage;
	import flash.display.StageDisplayState;
    import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.NetStatusEvent;
	import flash.events.AsyncErrorEvent;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
    import flash.media.Video;
	import flash.media.SoundTransform;
    import flash.net.NetConnection;
    import flash.net.NetStream;
	import flash.system.Capabilities;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import com.spikything.video.events.VideoEvent;
	import com.spikything.utils.Utils;
	
	public class VidPlayer extends Sprite {
		
		private const ROUNDING:Number 		= 18;
		private var nc:NetConnection;
		private var ns:NetStream;
		private var rtmpURL:String;
		private var vidURL:String;
		private var output:TextField;
		private var vidLength:int;
		private var playing:Boolean 		= true;
		private var atEnd:Boolean 			= false;
		private var maskRect:Sprite;
		private var videoWidth:int;
		private var videoHeight:int;
		private var video:Video;
		private var bufferTime:Number;
		private var muted:Boolean;
		private var curVolume:Number 		= 1;
		private var bwCounter:Number 		= 0;
		private var volume:Number;
		private var beacon:EventDispatcher;
		private var container:DisplayObject;
		private var px:Number;
		private var py:Number;
		private var sx:Number;
		private var sy:Number;
		private var stretched:Boolean 		= false;
		private var fixedSize:Boolean 		= false;
		private var drawLetterbox:Boolean 	= false;
		public var ready:Boolean 			= false;
		
		//
		public function VidPlayer (container:DisplayObject, rtmpURL:String, vidURL:String = null, videoWidth:int = 320, videoHeight:int = 240, muted:Boolean = false, volume:Number = 1, beacon:EventDispatcher = null, fixedSize:Boolean = false, bufferTime:Number = 5) {
			
			this.container = container;
			this.rtmpURL = rtmpURL;
			this.vidURL = vidURL;
			this.videoWidth = videoWidth;
			this.videoHeight = videoHeight;
			this.fixedSize = fixedSize;
			this.bufferTime = bufferTime;
			this.muted = muted;
			this.volume = volume;
			this.beacon = beacon;
			
			nc = new NetConnection();
			nc.client = { onBWDone:onBWDone };
			nc.objectEncoding = flash.net.ObjectEncoding.AMF0;
			nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
			nc.connect(rtmpURL);// , true);
			
			graphics.beginFill(0x000000);
			graphics.drawRoundRect(0, 0, videoWidth, videoHeight, ROUNDING, ROUNDING);
			maskRect = new Sprite();
			maskRect.graphics.beginFill(0x000000);
			maskRect.graphics.drawRoundRect(0, 0, videoWidth, videoHeight, ROUNDING, ROUNDING);
			maskRect.graphics.drawRect(0, videoHeight - ROUNDING, videoWidth, ROUNDING);
			addChild(maskRect);
			mask = maskRect;
			
			beacon.addEventListener(VideoEvent.DO_TOGGLEMUTE, toggleMute);
			beacon.addEventListener(VideoEvent.DO_SETVOLUME, setVolume);
			beacon.addEventListener(VideoEvent.DO_REBROADCAST, rebroadcast);
			beacon.addEventListener(VideoEvent.DO_PLAYPAUSE, playPause);
			beacon.addEventListener(VideoEvent.DO_SEEKTO, seekTo);
			addEventListener(Event.ENTER_FRAME, broadcastPosition);
			beacon.addEventListener(VideoEvent.DO_GO_FULLSCREEN, onVideoFullscreen);
			addEventListener(Event.ADDED_TO_STAGE, onAdded);
			broadcastLocation();
			
			if (!rtmpURL) initVideo();
			
		}
		
		//
		public function play (vidURL:String):void {
			trace("flvURL " + vidURL);
			this.vidURL = vidURL;
			if (!vidURL.length) return;
			if (!ready) return;
			ns.play(vidURL);
		}
		
		//
		public function set masked (state:Boolean):void {
			mask = state ? maskRect : null;
		}
		
		//
		protected function onAdded (e:Event):void {
			stage.addEventListener(Event.FULLSCREEN, onFullscreen);
		}
		
		//
		protected function onFullscreen (e:Event):void {
			if (stage.displayState == StageDisplayState.NORMAL) onExitFullscreen();
		}
		
		//
		protected function onExitFullscreen (e:Event=null):void {
			stage.displayState = StageDisplayState.NORMAL;
			graphics.clear();
			masked = true;
			restore();
			rebroadcast();
		}
		
		//
		protected function initVideo ():void {
			ns = new NetStream(nc);
			ns.client = this;// { onMetaData:onMetaData };// , onFCSubscribe:onFCSubscribe, onBWDone:onBWDone };
			ns.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
			ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
			video = new Video(videoWidth, videoHeight);
			video.attachNetStream(ns);
			addChild(video);
			
			output = new TextField();
			output.defaultTextFormat = new TextFormat("_sans", 10, 0xff0000);// 0xCC00CC);
			output.width = 280;
			output.height = 180;
			//output.blendMode = BlendMode.DIFFERENCE;
			addChild(output);
			
			var flashVer:String = Capabilities.version;
			var flashRev:Number = int(flashVer.split(",")[2]);
			doTrace("Flash Player version: " + flashVer);
			doTrace("Flash Player minor revision: " + flashRev);
			doTrace("System language: " + Capabilities.language);
			
			ns.bufferTime = bufferTime;
			ready = true;
		}
		
		
		
		//		
		protected function onNetStatus (evt:NetStatusEvent):void {
			
			doTrace(evt.info.code);
			switch (evt.info.code) {
			case "NetConnection.Connect.Success" :
				initVideo();
				if (vidURL && vidURL.length) play(vidURL);
				break;
				
			case "NetConnection.Connect.Failed" :
				//
				break;
				
			case "NetStream.Play.StreamNotFound" :
				doTrace(vidURL);
				break;
				
			case "NetStream.Play.Start" :
				playing = true;
				beacon.dispatchEvent(new VideoEvent(VideoEvent.INFO_PLAYING));
				removeEventListener(Event.ENTER_FRAME, onNearEnd);
				break;
				
			case "NetStream.Buffer.Empty" :
				removeEventListener(Event.ENTER_FRAME, onNearEnd);
				break;
				
			case "NetStream.Buffer.Flush" :
				if (!atEnd) addEventListener(Event.ENTER_FRAME, onNearEnd);
				break;

			case "NetStream.Play.Stop" :
				//ns.seek(0);
				break;
				
			case "NetStream.Pause.Notify" :
				beacon.dispatchEvent(new VideoEvent(VideoEvent.INFO_STOPPED));
				break;
				
			case "NetStream.Unpause.Notify" :
				beacon.dispatchEvent(new VideoEvent(VideoEvent.INFO_PLAYING));
				break;
				
			default :
				break;
			}
		}
		
		//
		protected function asyncErrorHandler (event:AsyncErrorEvent):void {
			doTrace(event.text);
		}
		
		//
		protected function onNearEnd (md:Object):void {
			if ((vidLength - ns.time < 0.2) && (ns.bufferLength == 0)) {
				atEnd = true;
				playing = false;
				removeEventListener(Event.ENTER_FRAME, onNearEnd);
				if (rtmpURL != null) ns.close();
				beacon.dispatchEvent(new VideoEvent(VideoEvent.INFO_STOPPED));
				doTrace("Finished");
			}
		}
		
		//
		public function onMetaData (md:Object):void {
			if (typeof md.duration != 'undefined') {
				vidLength = md.duration;
				var ve:VideoEvent = new VideoEvent(VideoEvent.INFO_METADATA);
				ve.duration = vidLength;
				beacon.dispatchEvent(ve);
			}
			if (typeof md.width != 'undefined') {
				if (!fixedSize) {
					video.width = md.width;
					video.height = md.height;
				}
				doTrace("Video dimensions: " + md.width + " x " + md.height);
				//Utils.fitInside(video, videoWidth, videoHeight);
				broadcastLocation();
			}
		}
		
		public function onBWDone ():Number { return bwCounter++ };
		public function onFCSubscribe (info:Object):void { };
		
		//
		protected function broadcastLocation ():void {
			var ve:VideoEvent = new VideoEvent(VideoEvent.INFO_VIDMOVED);
			ve.locationRect = this.getRect(container);
			beacon.dispatchEvent(ve);
		}
		
		//
		public function rebroadcast (e:Event=null):void {
			broadcastLocation();
			broadcastMute();
			broadcastPlaying();
			broadcastPosition();
		}
		
		//
		public function playPause (ve:VideoEvent):void {
			if (atEnd) {
				atEnd = false;
				doTrace("atEnd = " + atEnd);
				doTrace("playing = " + playing);
				beacon.dispatchEvent(new VideoEvent(VideoEvent.INFO_PLAYING));
				ns.seek(0);
				playing = true;
				broadcastPlaying();
				return;
			}
			if (playing) {
				ns.pause();
				playing = false;
			} else {
				ns.resume();
				playing = true;
			}
			broadcastPlaying();
		}
		
		protected function broadcastPlaying ():void {
			if (playing) {
				beacon.dispatchEvent(new VideoEvent(VideoEvent.INFO_PLAYING));
			} else {
				beacon.dispatchEvent(new VideoEvent(VideoEvent.INFO_STOPPED));
			}
		}
		
		//
		protected function seekTo (ve:VideoEvent):void {
			if (vidLength == 0) return;
			//removeEventListener(Event.ENTER_FRAME, onNearEnd);
			ns.seek(vidLength * ve.position);
			if (atEnd) {
				atEnd = false;
				ns.pause();
			}
		}
		
		//
		protected function broadcastPosition (e:Event=null):void {
			if (!playing) return;
			if (vidLength == 0) return;
			var ve:VideoEvent = new VideoEvent(VideoEvent.INFO_POSITION);
			ve.position = ns.time/vidLength;
			beacon.dispatchEvent(ve);
		}
		
		//
		protected function setVolume (ve:VideoEvent):void {
			curVolume = ve.volume;
			setVolumeInt(ve.volume);
			if (ve.volume) {
				muted = false;
				beacon.dispatchEvent(new VideoEvent(VideoEvent.INFO_UNMUTED));
			}
		}
		
		//
		protected function setVolumeInt (v:Number):void {
			var sT:SoundTransform = ns.soundTransform;
			sT.volume = v;
			ns.soundTransform = sT;
			var ve:VideoEvent = new VideoEvent(VideoEvent.INFO_VOLUME);
			ve.volume = v;
			beacon.dispatchEvent(ve);
		}
		
		//
		protected function toggleMute (ve:VideoEvent):void {
			muted = !muted;
			if (muted) {
				setVolumeInt(0);
			} else {
				setVolumeInt(curVolume);
			}
			broadcastMute();
		}
		
		//
		protected function broadcastMute ():void {
			if (muted) {
				beacon.dispatchEvent(new VideoEvent(VideoEvent.INFO_MUTED));
			} else {
				beacon.dispatchEvent(new VideoEvent(VideoEvent.INFO_UNMUTED));
			}
		}
		
		//
		public function vamoose (e:Event=null):void {
			//addEventListener(Event.ENTER_FRAME, fadeOut);
			remove();
		}
		
		//
		protected function remove ():void {
			removeEventListener(Event.ENTER_FRAME, fadeOut);
			ns.removeEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
			nc.removeEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
			ns.close();
			Utils.removeChild(parent, this);
		}
		
		//
		protected function fadeOut (e:Event):void {
			alpha -= .05;
			if (!muted) setVolumeInt(alpha * curVolume);
			if (alpha <= 0) remove();
		}
		
		//
		protected function onVideoFullscreen (e:Event = null):void {
			var shoddyPlayer:Boolean = false;
			graphics.clear();
			
			var aspect:Number;
			if (drawLetterbox) {
				aspect = Capabilities.screenResolutionX / Capabilities.screenResolutionY;
				var bw:Number = width;
				var bh:Number = width / aspect;
				graphics.beginFill(0x000000);
				graphics.drawRect(0, 0, width, width / aspect);
			}
			
			try {
				stage.fullScreenSourceRect = this.getRect(parent);
			} catch (e:Error) {
				shoddyPlayer = true;
			}
			
			stage.displayState = StageDisplayState.FULL_SCREEN;
			masked = false;
			if (shoddyPlayer) {
				fakeFullScreen();
			} else if (drawLetterbox) {
				video.y = ((width / aspect) - video.height) / 2;
			}
			rebroadcast();
		}
		
		//
		public function fakeFullScreen ():void {
			doTrace(">> fakeFullScreen()");
			var vidPos:Point = new Point(stage.x, stage.y);
			globalToLocal(vidPos);
			graphics.clear();
			//var vidPos:Rectangle = this.getRect(stage);
			//localToGlobal(vidPos);
			px = x;
			py = y;
			sx = width;
			sy = height;
			stretched = true;
			Utils.fitInside(this, Capabilities.screenResolutionX, Capabilities.screenResolutionY);// stage.stageWidth, stage.stageHeight);
			var dh:Number = Capabilities.screenResolutionY - height; //stage.stageHeight
			x = vidPos.x;
			y = vidPos.y;
			video.y = (Math.floor(dh*(video.height/height)) / 2);
			graphics.beginFill(0x000000);
			graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
		}
		
		//
		public function setPos (tx:Number, ty:Number):void {
			x = px = tx;
			y = px = ty;
		}

		//
		public function restore ():void {
			video.x = video.y = 0;
			if (stretched) {
				x = px;
				y = py;
				scaleX = scaleY = 1;
				//width = sx;
				//height = sy;
			}
			stretched = false;
		}

		
		//		
		public function doTrace (what:String):void {
			trace(what);
			if (output) {
				output.appendText(what + "\n");
				output.scrollV = output.maxScrollV;
			}
		}
	}
	
}